﻿using System;
using System.Linq.Expressions;

namespace Fly.Common.Extensions
{
    /// <summary>
    /// Expression 表达式树扩展类
    /// </summary>
    public static class ExpressionExtensions
    {
        #region OR连接
        /// <summary>
        /// 将两个个表达式树通过 OR 进行拼接
        /// </summary>
        /// <typeparam name="T">操作对象</typeparam>
        /// <param name="expr1">表达式一</param>
        /// <param name="expr2">表达式二</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
            return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
        }
        /// <summary>
        /// 将三个表达式树通过 OR 进行拼接
        /// </summary>
        /// <typeparam name="T">操作对象</typeparam>
        /// <param name="expr1">表达式一</param>
        /// <param name="expr2">表达式二</param>
        /// <param name="expr3">表达式三</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2, Expression<Func<T, bool>> expr3)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
            var temp = Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
            var invoke = Expression.Invoke(temp, expr3.Parameters);
            return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invoke), expr1.Parameters);
        }
        /// <summary>
        /// 将多个个表达式树通过 OR 进行拼接
        /// </summary>
        /// <typeparam name="T">操作对象</typeparam>
        /// <param name="expr1">表达式一</param>
        /// <param name="expr2">其他表达式数组</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, params Expression<Func<T, bool>>[] expr2)
        {
            Expression<Func<T, bool>> lambda = expr1;
            foreach (var expression in expr2)
            {
                var invoke = Expression.Invoke(expression, lambda.Parameters);
                lambda = Expression.Lambda<Func<T, bool>>(Expression.OrElse(lambda.Body, invoke), lambda.Parameters);
            }
            return lambda;
        } 
        #endregion

        #region And 连接
        /// <summary>
        ///  将两个表达式树通过 And 进行拼接
        /// </summary>
        /// <typeparam name="T">操作对象</typeparam>
        /// <param name="expr1">表达式一</param>
        /// <param name="expr2">表达式二</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
            return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
        }
        /// <summary>
        /// 将三个表达式树通过 AND 进行拼接
        /// </summary>
        /// <typeparam name="T">操作对象</typeparam>
        /// <param name="expr1">表达式一</param>
        /// <param name="expr2">表达式二</param>
        /// <param name="expr3">表达式三</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2, Expression<Func<T, bool>> expr3)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
            var lambda = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
            var invoke = Expression.Invoke(lambda, expr3.Parameters);

            return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(lambda.Body, invoke), expr3.Parameters);
        }
        /// <summary>
        /// 将多个表达式树通过 AND 进行拼接
        /// </summary>
        /// <typeparam name="T">操作对象</typeparam>
        /// <param name="expr1">表达式一</param>
        /// <param name="expr2">表达式数组</param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, params Expression<Func<T, bool>>[] expr2)
        {
            Expression<Func<T, bool>> lambda = expr1;
            foreach (var expression in expr2)
            {
                var invoke = Expression.Invoke(expression, lambda.Parameters);
                lambda = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(lambda.Body, invoke), lambda.Parameters);
            }
            return lambda;
        } 
        #endregion

    }
}
